
// ModelLoaderGLListener.java
// Andrew Davison, November 2006, ad@fivedots.coe.psu.ac.th

/* Display a loaded OBJ model, scaled and centered, 
   and possibly rotating.
*/

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.text.DecimalFormat;
import java.io.*;
import java.nio.*;

import javax.media.opengl.*;
import javax.media.opengl.glu.*;
import com.jogamp.opengl.util.gl2.GLUT; 
import com.jogamp.opengl.util.*;


public class Project4Listener extends GLSkeleton implements GLEventListener, KeyListener {
    private static final float INCR_MAX = 0.45f;   // for rotation increments
    private static final double Z_DIST = 7.0;      // for the camera position

    private GLU glu;

    GLMmodel model, building, ball1, ball2, ball3;

    private boolean closeUp;

    // rotation variables
    private float rotX, rotY = 90, rotZ;     // total rotations in x,y,z axes
    private float incrX, incrY, incrZ;  // increments for x,y,z rotations
    private float dispX = 0, dispY = 1, dispZ = 4;

    //My Variables
    long timer = 0;
    int action = 4;
    int move = 1;
    boolean reverse = false, left = false;
    private float ballX = 0, ballY = 0, ballZ = 0;
    
    float uparmR = 0;
    float lowarmR = 0;
    float uplegR = 0;
    float lowlegR = 0;
    float shoeR = 0;

    float uparmL = 0;
    float lowarmL = 0;
    float uplegL = 0;
    float lowlegL = 0;  
    float shoeL = 0;
  
    float mrkX, mrkY, mrkZ,
        mrkRX, mrkRY, mrkRZ;
 

    public Project4Listener() {
	     buildModels();
    }
	 
    public void init(GLAutoDrawable drawable) { 
        GL2 gl = drawable.getGL().getGL2(); 
        glu = new GLU();
        gl.setSwapInterval(0);   
        /* switches off vertical synchronization, for extra speed (maybe) */
        // initialize the rotation variables
        rotX = 0; rotY = 0; rotZ = 0;
        gl.glClearColor(0.17f, 0.65f, 0.92f, 1.0f);  // sky colour background for GLCanvas
        gl.glEnable(gl.GL_DEPTH_TEST);
        addLight(gl);
    }
	 
	 public void buildModels() {
        // load the OBJ model
        model = GLM.glmReadOBJ("models/al.obj" );                       
        GLM.glmUnitize(model);
        GLM.glmFacetNormals(model);
        GLM.glmVertexNormals(model, 90.0f);  
        
        building = GLM.glmReadOBJ("models/mpcottage.obj");
        GLM.glmUnitize(building);
        GLM.glmFacetNormals(building);
        GLM.glmVertexNormals(building, 90.0f);
        
        ball1 = GLM.glmReadOBJ("models/ball.obj");
        GLM.glmUnitize(ball1);
        GLM.glmFacetNormals(ball1);
        GLM.glmVertexNormals(ball1, 90.0f);
    } // end of init()


    private void addLight(GL2 gl) {
        // two white light sources 
        // enable light sources
       gl.glEnable(gl.GL_LIGHTING);
       gl.glEnable(gl.GL_LIGHT0);
       gl.glEnable(gl.GL_LIGHT1);

       float[] whiteLight = {1.0f, 1.0f, 1.0f, 1.0f};  // bright white

       float lightPos[] = {10.0f, 10.0f, -10.0f, 1.0f}; 
       // light located at the right, top, and back 
       // light 0 has white ambient, diffuse, and specular components by default
       gl.glLightfv(gl.GL_LIGHT0, gl.GL_POSITION, lightPos, 0);
    
       float lightPos1[] = {-10.0f, -10.0f, 10.0f, 1.0f};
       // light located at the left, bottom, and front 
       gl.glLightfv(gl.GL_LIGHT1, gl.GL_DIFFUSE, whiteLight, 0);  // diffuse white
       gl.glLightfv(gl.GL_LIGHT1, gl.GL_POSITION, lightPos1, 0);
    }  

    int width, height;
  
    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
        // called when the drawable component is moved or resized
        GL2 gl = drawable.getGL().getGL2();

        if (height == 0)
            height = 1;    // to avoid division by 0 in aspect ratio below

        this.width = width;
        this.height = height; 

        gl.glViewport(x, y, width, height);  // size of drawing area 
    } 
  
    public void reset(){
		dispX = 0;
		dispY = 1;
		dispZ = 4;
    	rotY = 0;
    	rotX = 0;
    	rotZ = 0;
        uparmR = 0;
        lowarmR = 0;
        uplegR = 0;
        lowlegR = 0;
			 shoeR = 0;
        uparmL = 0;
        lowarmL = 0;
        uplegL = 0;
        lowlegL = 0;       
			 shoeL = 0;  
    }
	 
    public void display(GLAutoDrawable drawable) {
        // the model is rotated and rendered

        GL2 gl = drawable.getGL().getGL2(); 
        gl.glMatrixMode(gl.GL_PROJECTION);
        gl.glLoadIdentity();
        float dim = 0.33f;
     
        if (closeUp) 
            gl.glOrtho(-dim,dim,-dim,dim,-5,50);   
        else
            glu.gluPerspective(45.0, (float)width/(float)height, 1, 100);  
                             // fov, aspect ratio, near & far clipping planes  
 
        gl.glMatrixMode(gl.GL_MODELVIEW);
        gl.glLoadIdentity();

        // clear colour and depth buffers
        gl.glClearColor(0.2f,0.2f,0.2f,0);
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT);
        gl.glLoadIdentity();

        glu.gluLookAt(0,0,Z_DIST, 0,0,0, 0,1,0);   // position camera
      
        gl.glPushMatrix();
        gl.glTranslatef(0,2,-9);
        gl.glScalef(6f,6f,6f);
        gl.glRotatef(90,0,1,0);
        GLM.glmDraw(building, GLM.GLM_SMOOTH | GLM.GLM_MATERIAL, gl);
        gl.glPopMatrix();          
        
        
        gl.glTranslatef(dispX+0f,dispY+-0.9f,dispZ+0.0f);
        
        gl.glRotatef(rotX, 1.0f, 0.0f, 0.0f);
        gl.glRotatef(rotY, 0.0f, 1.0f, 0.0f);
        gl.glRotatef(rotZ, 0.0f, 0.0f, 1.0f);		  

        model.clearTransforms();
        
        
        //TODO  My Stuff:
        //Al's ankle, knee, hip, elbow and shoulder joints should all 
        //move as Al walks
        gl.glScalef(.5f, .5f, .5f);
        timer += 1;
                
        if(action == 0 || action == 1 || action == 4){//Key Press P / O
	        
	        dispY = (float) (1+Math.cos(timer*.05)/45);
	
	        if(action == 0){
	        	if(reverse && action == 0){
	        		gl.glRotatef(-90, 0, 1f, 0);
			        if(dispX < -1)
			        	dispX = 1;
			        dispX -= .003;
	        	}
	        	else{
	        		gl.glRotatef(90, 0, 1f, 0);
			        if(dispX > 1)
			        	dispX = -1;
			        dispX += .003;
	        	}
	        }
	        else if(action == 1){
	        	if(reverse){
	        		gl.glRotatef(180, 0, 1f, 0);
			        if(dispZ < 0)
			        	dispZ = 5;
			        dispZ -= .003;
	        	}
	        	else{
			        if(dispZ > 5)
			        	dispZ = 0;
			        dispZ += .003;
	        	}
	        }
	        else if(action == 4){
	        	if(reverse){
		        	gl.glRotatef((float)(90+timer*.28648),0,1,0);
		        	dispZ = (float)(4+Math.cos(timer * .005));
		        	dispX = (float)(Math.sin(timer * .005));
	        	}
	        	else{
		        	gl.glRotatef(-(float)(timer*.28648),0,1,0);
		        	dispZ = (float)(4+Math.sin(timer * .005));
		        	dispX = (float)(Math.cos(timer * .005));
	        	}
	        }
	        
	        shoeL = (float) Math.cos(timer*.025)*10;
	        shoeR = -(float) Math.cos(timer*.025)*10;
	        lowarmL = -(float) Math.sin(timer*.025)*15;
	        lowarmR = (float) Math.sin(timer*.025)*15;
	        uparmR = -(float) Math.sin(timer*.025)*30;
	        uparmL = (float) Math.sin(timer*.025)*30;
	        uplegL = -(float) Math.sin(timer*.025)*40;
	        uplegR = (float) Math.sin(timer*.025)*40;
	        lowlegL = (float) Math.cos(timer*.025)*15;
	        lowlegR = -(float) Math.cos(timer*.025)*15;
        }
        //p o i j m
        
        else if(action == 2){
        	if(move == 1){
        		uparmR = -(float)(Math.sin(timer*.02)*90);
        		if(timer*.02 > Math.PI/2){
        			timer = 0;
        			move = 2;
        		}
        	}
        	else if(move == 2){
	        	uplegL = -(float)(Math.sin(timer*.02)*75);
	        	uplegR = -(float)(Math.sin(timer*.02)*75);
	        	rotX = (float)(Math.sin(timer*.02)*65);
	        	dispY = (float)(1-Math.sin(timer*.02)/10);
	        	if(timer*.02 > Math.PI/2){
	        		timer = 0;
	        		move = 3;
	        	}
        	}
        	else if(move == 3){
        		uparmL = -(float)(Math.sin(timer*.02)*90);
	        	if(timer*.02 > Math.PI/2){
	        		move = 4;
	        	}
        	}
        	else if(move == 4){
	        	uplegL = -(float)(Math.sin(timer*.02)*75);
	        	uplegR = -(float)(Math.sin(timer*.02)*75);
	        	rotX = (float)(Math.sin(timer*.02)*65);
	        	dispY = (float)(1-Math.sin(timer*.02)/10);
	        	if(timer*.02 > Math.PI){
	        		timer = (long) ((Math.PI/2)/.02);
	        		move = 5;
	        	}
        	}
        	else if(move == 5){
        		uparmR = -(float)(Math.sin(timer*.02)*90);
	        	if(timer*.02 > Math.PI){
	        		timer = (long) ((Math.PI/2)/.02);
	        		move = 6;
	        	}
        	}
        	else if(move == 6){
        		uparmL = -(float)(Math.sin(timer*.02)*90);
	        	if(timer*.02 > Math.PI){
	        		timer = 0;
	        		move = 1;
	        	}
        	}
        }
        
        //Juggle?
        else if(action == 3){
//        	gl.glRotatef(90, 0, 1, 0);
        	lowarmR = 15;
        	lowarmL = -15;
        	uparmR = -30;
        	uparmL = -30;
        	if(!left){
		        lowarmR = (float) (15 + Math.sin(timer*.025)*15);
		        uparmR = -(float) (30 + Math.sin(timer*.025)*30);
		        if(timer*.025 > 2 * Math.PI){
		        	left = !left;
		        }
        	}
        	else if(left){
		        lowarmL = -(float) (15 + Math.sin(timer*.025)*15);
		        uparmL = -(float) (30 + Math.sin(timer*.025)*30);
		        if(timer*.025 > 4 * Math.PI){
		        	timer = 0;
		        	left = !left;
		        }
        	}
	        gl.glPushMatrix();
	        gl.glTranslatef((float)(Math.sin(Math.PI/4*3 + timer*.0125)/1.5),(float)(.7+Math.sin(timer*.025)),.3f);
	        gl.glScalef(.1f,.1f,.1f);
	        GLM.glmDraw(ball1, GLM.GLM_SMOOTH | GLM.GLM_MATERIAL, gl);
	        gl.glPopMatrix();   
        }
        
        String[] parts2 = { "uplegR", "lowlegR", "shoe1R", "shoe2R", "shoe3R", "shoe4R" };
        for (int i = 0; i < parts2.length; i++) {     
            model.addTransform(parts2[i],new GLMtransform(GLMtransform.TRA,-0.14f,-0.31f,0.00f,0));      
            model.addTransform(parts2[i],new GLMtransform(GLMtransform.ROT,1,0,0,uplegR));
            model.addTransform(parts2[i],new GLMtransform(GLMtransform.TRA,0.14f,0.31f,0.00f,0));     
        }
      
        String[] parts1 = { "lowlegR", "shoe1R", "shoe2R", "shoe3R", "shoe4R" };
        for (int i = 0; i < parts1.length; i++) {
            model.addTransform(parts1[i],new GLMtransform(GLMtransform.TRA,-0.25f,-0.57f,0.06f,0));         
            model.addTransform(parts1[i],new GLMtransform(GLMtransform.ROT,1,0,0,lowlegR));
            model.addTransform(parts1[i],new GLMtransform(GLMtransform.TRA,0.25f,0.57f,-0.06f,0));
        } 

        String[] parts = { "shoe1R", "shoe2R", "shoe3R", "shoe4R" };
        for (int i = 0; i < parts.length; i++) {
            model.addTransform(parts[i],new GLMtransform(GLMtransform.TRA,-0.26f,-0.91f,0.04f,0));       
            model.addTransform(parts[i],new GLMtransform(GLMtransform.ROT,1,0,0,shoeR));
            model.addTransform(parts[i],new GLMtransform(GLMtransform.TRA,0.26f,0.91f,-0.04f,0));
        }
      
      
        String[] parts2L = { "uplegL", "lowlegL", "shoe1L", "shoe2L", "shoe3L", "shoe4L" };
        for (int i = 0; i < parts2L.length; i++) {    
            model.addTransform(parts2L[i],new GLMtransform(GLMtransform.TRA,0.14f,-0.31f,0.00f,0));      
            model.addTransform(parts2L[i],new GLMtransform(GLMtransform.ROT,1,0,0,uplegL));
            model.addTransform(parts2L[i],new GLMtransform(GLMtransform.TRA,-0.14f,0.31f,0.00f,0));      
        }
      
        String[] parts1L = { "lowlegL", "shoe1L", "shoe2L", "shoe3L", "shoe4L" };
        for (int i = 0; i < parts1L.length; i++) {
            model.addTransform(parts1L[i],new GLMtransform(GLMtransform.TRA,0.25f,-0.57f,0.06f,0));         
            model.addTransform(parts1L[i],new GLMtransform(GLMtransform.ROT,1,0,0,lowlegL));
            model.addTransform(parts1L[i],new GLMtransform(GLMtransform.TRA,-0.25f,0.57f,-0.06f,0));
        }      
        String[] partsL = { "shoe1L", "shoe2L", "shoe3L", "shoe4L" };
        for (int i = 0; i < partsL.length; i++) {
            model.addTransform(partsL[i],new GLMtransform(GLMtransform.TRA,0.26f,-0.91f,0.04f,0));       
            model.addTransform(partsL[i],new GLMtransform(GLMtransform.ROT,1,0,0,shoeL));
            model.addTransform(partsL[i],new GLMtransform(GLMtransform.TRA,-0.26f,0.91f,-0.04f,0));
        }      

        float[] upArmVect = { -.34f - -.67f, .26f - .11f, -.13f - -.12f  };
        float[] lowArmVect = { -.67f - -.76f, .11f - -.07f, -.12f - .07f };
        float[] elbRotAxis = new float[3];            
        GLM.glmCross(upArmVect,lowArmVect,elbRotAxis,0);          

                        
        String[] armParts2 = { "uparmR", "lowarmR", "handR", "sleaveR" };
        for (int i = 0; i < armParts2.length; i++) {                 
            model.addTransform(armParts2[i],new GLMtransform(GLMtransform.TRA,-0.34f,0.26f,-0.13f,0));         
            model.addTransform(armParts2[i],new GLMtransform(GLMtransform.ROT,1,0,0,uparmR));
            model.addTransform(armParts2[i],new GLMtransform(GLMtransform.TRA,0.34f,-0.26f,0.13f,0));       
        }
      
        String[] armParts1 = { "lowarmR", "handR", "sleaveR" };
        for (int i = 0; i < armParts1.length; i++) {                 
            model.addTransform(armParts1[i],new GLMtransform(GLMtransform.TRA,-0.67f,0.11f,-0.12f,0));         
            model.addTransform(armParts1[i],new GLMtransform(GLMtransform.ROT,
                elbRotAxis[0], elbRotAxis[1], elbRotAxis[2], lowarmR));
            model.addTransform(armParts1[i],new GLMtransform(GLMtransform.TRA,0.67f,-0.11f,0.12f,0));       
        }   
      
        String[] armParts2L = { "uparmL", "lowarmL", "handL", "sleaveL" };
        for (int i = 0; i < armParts2.length; i++) {                 
            model.addTransform(armParts2L[i],new GLMtransform(GLMtransform.TRA,0.34f,0.26f,-0.13f,0));         
            model.addTransform(armParts2L[i],new GLMtransform(GLMtransform.ROT,1,0,0,uparmL));
            model.addTransform(armParts2L[i],new GLMtransform(GLMtransform.TRA,-0.34f,-0.26f,0.13f,0));        
        }
      
        String[] armParts1L = { "lowarmL", "handL", "sleaveL" };
        for (int i = 0; i < armParts1.length; i++) {                 
            model.addTransform(armParts1L[i],new GLMtransform(GLMtransform.TRA,0.67f,0.11f,-0.12f,0));         
            model.addTransform(armParts1L[i],new GLMtransform(GLMtransform.ROT,
                -elbRotAxis[0], elbRotAxis[1], elbRotAxis[2], lowarmL));
            model.addTransform(armParts1L[i],new GLMtransform(GLMtransform.TRA,-0.67f,-0.11f,0.12f,0));        
        }         

        GLM.glmDraw(model, GLM.GLM_SMOOTH | GLM.GLM_MATERIAL, gl);
     

      
        if (model.wireFrame) {
      
            float[] emi = { 1,1,1};
            float[] emiOff = { 0,0,0};          
            gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_EMISSION, FloatBuffer.wrap(emi));       
            gl.glTranslatef(mrkX,mrkY,mrkZ);
            gl.glRotatef(mrkRX,1,0,0); 
            gl.glRotatef(mrkRY,0,1,0);
            gl.glRotatef(mrkRZ,0,0,1); 
                                
            gl.glColor3f(1,1,1);         
            gl.glBegin(gl.GL_LINES);
            gl.glVertex3f(-0.5f,0,0);   // an x-axis
            gl.glVertex3f( 0.5f,0,0);
            gl.glVertex3f(0,-0.5f,0);   // a y-axis
            gl.glVertex3f(0, 0.5f,0);
            gl.glVertex3f(0,0,0);       // a z-axis
            gl.glVertex3f(0,0,1);            
            gl.glEnd();
          
            gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_EMISSION, FloatBuffer.wrap(emiOff)); 
            System.out.printf("X = %f, Y = %f, Z = %f; RX = %f, RY = %f, RZ = %f\n",
                               mrkX,mrkY,mrkZ,mrkRX,mrkRY,mrkRZ);             
                  
        }   

        gl.glFlush();
    } // end of display


    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, 
                               boolean deviceChanged) {
	 }
	 
    public void dispose(javax.media.opengl.GLAutoDrawable dr) {
	 }	 
  
    public void keyTyped(KeyEvent key) {
    }

    public void controlKey(KeyEvent key) {
        if (key.isShiftDown()) 

            switch (key.getKeyCode()) {
                case KeyEvent.VK_DOWN:
                    mrkRX += 1f;
                    break;
                case KeyEvent.VK_UP:
                    mrkRX += -1f;
                    break;
                case KeyEvent.VK_LEFT:
                    mrkRY += -1f;
                    break;
                case KeyEvent.VK_RIGHT:
                    mrkRY += 1f;
                    break;
                case KeyEvent.VK_PAGE_DOWN:
                    mrkRZ += -1f;
                    break;
                case KeyEvent.VK_PAGE_UP:
                    mrkRZ += 1f;
                    break;    

        } else {  // no shift key
        
            switch (key.getKeyCode()) {
                case KeyEvent.VK_DOWN:
                    mrkY += -0.01f;
                    break;
                case KeyEvent.VK_UP:
                    mrkY +=  0.01f;
                    break;
                case KeyEvent.VK_LEFT:
                    mrkX += -0.01f;
                    break;
                case KeyEvent.VK_RIGHT:
                    mrkX += 0.01f;
                    break;
                case KeyEvent.VK_PAGE_DOWN:
                    mrkZ += 0.01f;
                    break;
                case KeyEvent.VK_PAGE_UP:
                    mrkZ += -0.01f;
                    break;    
            }         
        }
        refresh();        
    }
    
    public void keyPressed(KeyEvent key) {
        if (key.isControlDown()) {
           controlKey(key);
           return; 
        } 
        switch (key.getKeyCode()) {
        //My Key Listeners
        	case KeyEvent.VK_I:
        		if(action == 0 || action == 1 || action == 4)
	        		reverse = !reverse;
        		break;
        		
        	case KeyEvent.VK_N:
        		reset();
        		action = 4;
        		timer = 0;
        		break;
        		
        	case KeyEvent.VK_J:
        		reset();
        		action = 3;
        		timer = 0;
        		break;
        		
        	case KeyEvent.VK_P:
        		reset();
        		action = 0;
        		timer = 0;
        		break;
        		
        	case KeyEvent.VK_O:
        		reset();
        		action = 1;
        		timer = 0;
        		break;
        		
        	case KeyEvent.VK_M:
        		reset();
        		action = 2;
        		timer = 0;
        		move = 1;
        		break;
        
            case KeyEvent.VK_C:
                //model.toggleConstantShading();
                break;
                
            case KeyEvent.VK_W:
                model.wireFrame = ! model.wireFrame; 
                break;  
                
            case KeyEvent.VK_Z:
            	rotY = 0;
            	rotX = 0;
            	rotZ = 0;
                uparmR = 0;
                lowarmR = 0;
                uplegR = 0;
                lowlegR = 0;
					 shoeR = 0;
                uparmL = 0;
                lowarmL = 0;
                uplegL = 0;
                lowlegL = 0;       
					 shoeL = 0;        
                break;                              
            
            case KeyEvent.VK_ESCAPE:
                closeUp = !closeUp;
                break;
                
            case KeyEvent.VK_DOWN:
                rotX += 1;
                break;
            case KeyEvent.VK_UP:
                rotX -= 1;
                break;
            case KeyEvent.VK_LEFT:
                rotY += -1;
                break;
            case KeyEvent.VK_RIGHT:
                rotY += 1;
                break;
            case KeyEvent.VK_PAGE_DOWN:
                rotZ += -1;
                break;
            case KeyEvent.VK_PAGE_UP:
                rotZ += 1;
                break;               
                
            case KeyEvent.VK_U:
                dispY += 0.2;
                break;
                
            case KeyEvent.VK_D:
                dispY -= 0.2;          
                break;      
                
            case KeyEvent.VK_L:
                dispX -= 0.2;
                break;
                
            case KeyEvent.VK_R:
                dispX += 0.2;          
                break;               
                
            case KeyEvent.VK_B:
                dispZ -= key.isShiftDown() ? 1 : 0.1f;
                break;
                
            case KeyEvent.VK_F:
                dispZ += key.isShiftDown() ? 1 : 0.1f;          
                break;               
                                  
            case KeyEvent.VK_1:
				    uplegR += key.isShiftDown() ? 1 : -1;        
                break;
   
            case KeyEvent.VK_2:
				    lowlegR += key.isShiftDown() ? 1 : -1;              
                break;      
					 
            case KeyEvent.VK_3:   
				    shoeR += key.isShiftDown() ? 1 : -1;  							          
                break;  					                                        

            case KeyEvent.VK_4:
				    uparmR += key.isShiftDown() ? 1 : -1;  						            
                break;

            case KeyEvent.VK_5: 
				    lowarmR += key.isShiftDown() ? 1 : -1;  						            
                break;                   

            case KeyEvent.VK_6:
				    uplegL += key.isShiftDown() ? 1 : -1;  							           
                break;
   
            case KeyEvent.VK_7:
				    lowlegL += key.isShiftDown() ? 1 : -1;  							             
                break; 
					 
            case KeyEvent.VK_8:  
				    shoeL += key.isShiftDown() ? 1 : -1;  							           
                break;					                                             

            case KeyEvent.VK_9:   
				    uparmL += key.isShiftDown() ? 1 : -1;  							         
                break;

            case KeyEvent.VK_0:
				    lowarmL += key.isShiftDown() ? 1 : -1;  							             
                break;

            default:
                break;
        }

       refresh();
    }
    
    public void keyReleased(KeyEvent key) {
    }
  

 
 } 

